fix(controlplane): enforce project-scoped RBAC on AttestationService/GetContract - #3401
Conversation
…GetContract AttestationService/GetContract resolved the workflow org-scoped without checking the caller's rights on the requested project, letting a project-scoped API token read another project's workflow metadata and full contract schema through the attestation JWT path, which the authz middleware skips. Apply the same project authorization the sibling Init/Store/Cancel handlers already perform, via userHasPermissionOnProject with PolicyWorkflowRead, after the workflow is resolved. Export usercontext.WithRobotAccount so the service-layer integration tests can build the same context the attestation middlewares produce. Assisted-by: OpenCode Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: ses_f92bd9943ffeCzB41EHYLejTZR
AI Session Checks — 🟡 74% · ✅ 0 failing
|
| Status | Attribution | File | Lines |
|---|---|---|---|
| created | ai | app/controlplane/internal/service/attestation_integration_test.go |
+180 / -0 |
| modified | ai | app/controlplane/internal/service/attestation.go |
+5 / -0 |
| modified | ai | app/controlplane/internal/usercontext/robotaccount_middleware.go |
+3 / -2 |
| modified | ai | app/controlplane/internal/usercontext/apitoken_middleware.go |
+1 / -1 |
| modified | ai | app/controlplane/internal/usercontext/currentuser_middleware.go |
+1 / -1 |
| modified | ai | app/controlplane/internal/usercontext/federated_middleware.go |
+1 / -1 |
Policies (4)
| Status | Policy | Material | Messages |
|---|---|---|---|
| ✅ Passed | ai-config-ai-agents-allowed |
ai-coding-session-ses-f9 |
- |
| ✅ Passed | ai-config-no-dangerous-commands |
ai-coding-session-ses-f9 |
- |
| ✅ Passed | ai-config-no-secrets |
ai-coding-session-ses-f9 |
- |
| ✅ Passed | ai-config-mcp-servers-allowed |
ai-coding-session-ses-f9 |
- |
Security Checks — ✅ 7 passing
✅ secret-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | secrets-detection |
- |
✅ sast-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | owasp-top10-2025 |
- |
| ✅ Passed | sast |
- |
| ✅ Passed | cwe-top25 |
- |
| ✅ Passed | cwe-top26-40-cusp |
- |
PR info
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | pr-description-required |
- |
| ✅ Passed | pr-user-story-linked |
- |
⏭️ 3 scans not applied
| Scan | Reason |
|---|---|
vulnerability-scan |
no manifest/lockfile changed |
github-actions-scan |
no workflow files changed |
iac-scan |
no IaC files changed |
Powered by Chainloop and Chainloop Trace
There was a problem hiding this comment.
No issues found across 6 files
Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.
Re-trigger cubic
A project-scoped API token could call AttestationService/GetContract with another project's name and receive that project's workflow metadata and full contract schema. Attestation endpoints are authenticated through the attestation JWT path and skipped by the authz middleware, so the handler is the only authorization point, and GetContract was the lone attestation handler that never checked the caller's rights on the requested project.
This adds the same project authorization the sibling Init, Store and Cancel handlers already perform, applied after the workflow is resolved so the project reference is validated first. A project-scoped token now only sees workflows and contracts in its own project, while org-scoped tokens, workflow-scoped tokens, user, legacy robot account and federated callers keep their existing behavior.
To let the service-layer integration tests reproduce the context the attestation middlewares produce, usercontext.WithRobotAccount is exported alongside the existing CurrentRobotAccount reader.
Fixes PFM-6717
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri